Skip to main content

回调验签

在开发者中心「企业回调地址」保存后,平台会签发 AppId 与 Secret。之后每次推送(含页面上的「测试」)都会带 HMAC 签名。请在接收服务里验签通过后再处理业务。

info

AppId 只用来识别是哪份订阅,会出现在 JSON 和请求头里,不能当唯一放行条件。

Secret 只用于算签名,不要写进前端、不要打进日志。

请求约定​

推送均为 HTTPS POST,Content-Type: application/json。

请求头说明
X-Gausium-App-Id与开发者中心 AppId 一致
X-Gausium-Timestamp毫秒时间戳。与本地相差超过 5 分钟则拒绝
X-Gausium-Nonce随机串。建议缓存 10 分钟去重
X-Gausium-Signaturesha256=<hex>,HMAC-SHA256(Secret)

签名字符串必须用原始 body 字节(不要先 parse 再序列化):

timestamp + "\n" + nonce + "\n" + rawBody

计算 HMAC-SHA256(Secret, 签名字符串),输出小写 hex,与去掉 sha256= 后的值做恒定时间比较。

测试包带 payload.test = true,验过返回 200 即可,不要当真实告警或任务。

校验步骤​

  1. 只暴露 https:// 接收地址。
  2. 四个请求头缺一即拒绝。
  3. X-Gausium-App-Id 等于本订阅 AppId。
  4. |nowMs - timestamp| < 5 × 60 × 1000。
  5. 按上一节算签名并恒定时间比较。
  6. 通过后再解析 JSON;失败返回 4xx,不要落库。

存量仅 appId + payload、未启用 Secret 的订阅仍只按 body 里的 AppId 识别;新签发订阅必须验签。

相关页面​

  • 事件推送:事件推送的字段说明与报警恢复判定流程。
  • 任务报告推送:任务报告的字段说明、时间精度差异与历史报告查询方式。

示例​

Python​

import hmac, hashlib, time

def verify(headers, raw_body: bytes, app_id: str, secret: str) -> bool:
got_app = headers.get("X-Gausium-App-Id", "")
ts = headers.get("X-Gausium-Timestamp", "")
nonce = headers.get("X-Gausium-Nonce", "")
sig = headers.get("X-Gausium-Signature", "")
if got_app != app_id or not ts or not nonce or not sig.startswith("sha256="):
return False
if abs(int(time.time() * 1000) - int(ts)) > 5 * 60 * 1000:
return False
msg = f"{ts}\n{nonce}\n".encode() + raw_body
expect = "sha256=" + hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
return hmac.compare_digest(expect, sig)

Node.js​

const crypto = require("crypto");

function verify(headers, rawBodyBuf, appId, secret) {
const gotApp = headers["x-gausium-app-id"];
const ts = headers["x-gausium-timestamp"];
const nonce = headers["x-gausium-nonce"];
const sig = headers["x-gausium-signature"] || "";
if (gotApp !== appId || !ts || !nonce || !sig.startsWith("sha256=")) return false;
if (Math.abs(Date.now() - Number(ts)) > 5 * 60 * 1000) return false;
const msg = Buffer.concat([Buffer.from(`${ts}\n${nonce}\n`), rawBodyBuf]);
const expect = "sha256=" + crypto.createHmac("sha256", secret).update(msg).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expect), Buffer.from(sig));
}

Java​

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
mac.update((timestamp + "\n" + nonce + "\n").getBytes(StandardCharsets.UTF_8));
mac.update(rawBody);
String expect = "sha256=" + Hex.encodeHexString(mac.doFinal());
return MessageDigest.isEqual(
expect.getBytes(StandardCharsets.UTF_8),
signature.getBytes(StandardCharsets.UTF_8));

连通性测试​

开发者中心「测试」会向页面填写的地址 POST 一次 payload.test = true 的探测包,只发一次,不修改配置。已有 Secret 时,探测请求带与正式推送完全相同的四个签名头;可用它先验证你的验签逻辑。

若对方返回非 2xx 或连不上,接口本身仍返回 code = 0,结果放在 data.httpCode 与 data.reachable 中,便于页面区分「地址不可达」与「参数不合法」。

相关错误码​

错误码说明触发条件
20059回调地址必填测试时地址为空
20060回调地址不合法地址不是以 http:// 或 https:// 开头,或超过 150 个字符